﻿//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using System.Globalization;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Microsoft.ResourceManagement.Samples.ResourceManagementClient.Utilities
{
    /// <summary>
    /// Singleton class that provides consistent treatment of resource management XML
    /// Use this class as the authoritative guide to namespaces, schema, and checking for faults!
    /// </summary>
    public sealed class StaticSchemaManager
    {
        private static StaticSchemaManager instance;
        public static StaticSchemaManager Instance
        {
            get
            {
                if (null == instance)
                {
                    instance = new StaticSchemaManager();
                }
                return instance;
            }
        }

        private XmlDocument resourceManagementSchemaDoc;
        private XmlNamespaceManager resourceManagementNamespaceManager;

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", MessageId = "System.Xml.XmlNode")]
        public XmlDocument ResourceManagementSchemaDoc
        {
            get
            {
                return this.resourceManagementSchemaDoc;
            }
        }
        
        public XmlNamespaceManager ResourceManagementNamespaceManager
        {
            get
            {
                return this.resourceManagementNamespaceManager;
            }
        }
        private StaticSchemaManager()
        {
            this.resourceManagementSchemaDoc = new XmlDocument();
            this.resourceManagementNamespaceManager = new XmlNamespaceManager(this.resourceManagementSchemaDoc.NameTable);
            // these are the commonly-used namespaces in ILM
            this.resourceManagementNamespaceManager.AddNamespace("rm", Constants.NamespaceResourceManagement);
            this.resourceManagementNamespaceManager.AddNamespace("s", Constants.NamespaceSoap);
            this.resourceManagementNamespaceManager.AddNamespace("da", Constants.NamespaceDirectoryAccess);
            this.resourceManagementNamespaceManager.AddNamespace("xs", Constants.NamespaceXsd);
            this.resourceManagementNamespaceManager.AddNamespace("xsi", Constants.NamespaceXsdInstance);
            this.resourceManagementNamespaceManager.AddNamespace("wsa", Constants.NamespaceWSAddressing);
            this.resourceManagementNamespaceManager.AddNamespace("wsen", Constants.NamespaceWSEnumeration);
            try
            {
                // this should not fail as the xsd is included in this project
                this.resourceManagementSchemaDoc.Load(Settings.Default.IlmSchemaFilename);
            }
            catch (System.IO.IOException)
            {
                // unsure how to handle this case
                // but if the file is missing, the caller needs
                // to know we can't recover
                //throw;
            }
        }
        public String GetAttributeType(String attribName)
        {
            XmlNode elementDefNode = this.resourceManagementSchemaDoc.SelectSingleNode(
                String.Format(CultureInfo.CurrentCulture, "//xs:element[@name='{0}']", attribName), this.resourceManagementNamespaceManager);

            if (null == elementDefNode)
                return null;

            XmlAttribute typeAttrib = elementDefNode.Attributes["type"];
            if (null == typeAttrib || null == typeAttrib.Value)
            {
                return Constants.DataTypeXSString;
            }
            else
            {
                return typeToString(typeAttrib.Value);
            }
        }
        /// <summary>
        /// Returns whether the given attribute is multi-valued
        /// </summary>
        /// <param name="attribName"></param>
        /// <returns></returns>
        public bool IsMultiValued(String attribName)
        {
            XmlNode elementDefNode = this.resourceManagementSchemaDoc.SelectSingleNode(
                String.Format(CultureInfo.CurrentCulture, "//xs:element[@name='{0}']", attribName), this.resourceManagementNamespaceManager);

            if (null == elementDefNode)
                return false;

            XmlAttribute maxOccursAttrib = elementDefNode.Attributes["type"];
            if (null == maxOccursAttrib)
                return false;

            return maxOccursAttrib.Value.Equals("unbounded");
        }

        private static String typeToString(String typeName)
        {
            typeName = typeName.ToUpperInvariant();
            switch (typeName)
            {
                case "XS:BOOLEAN":
                    return Constants.DataTypeXSBoolean;
                case "XS:STRING":
                    return Constants.DataTypeXSString;
                case "XS:INTEGER":
                    return Constants.DataTypeXSInteger;
                case "RM:REFERENCETYPE":
                    return Constants.DataTypeXSReference;
                case "RM:REFERENCECOLLECTIONTYPE":
                    return Constants.DataTypeXSReference;
                default:
                    return Constants.DataTypeXSString;
            }
        }
        /// <summary>
        /// Throws an exception if the returned message is a fault message
        /// </summary>
        /// <param name="message"></param>
        public void CheckFault(Message message)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(message.ToString());

            if (null != doc.SelectSingleNode("//s:Fault", this.resourceManagementNamespaceManager))
            {
                throw new System.ServiceModel.FaultException(message.ToString());
            }
        }
    }
}
